package org.nvframe.component.physics.state;

import org.jbox2d.collision.MassData;
import org.jbox2d.collision.shapes.CircleDef;
import org.jbox2d.collision.shapes.PolygonDef;
import org.jbox2d.collision.shapes.ShapeDef;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.World;

import org.newdawn.slick.geom.Vector2f;
import org.nvframe.component.AbstractComponent;
import org.nvframe.component.Component;
import org.nvframe.component.state.Position;
import org.nvframe.entity.Entity;
import org.nvframe.manager.ConfigManager;
import org.nvframe.manager.RegisterManager;
import org.nvframe.util.Settings;

/**
 * 
 * @author Nik Van Looy
 */
public class PhysicsPosition extends AbstractComponent implements Position {

	private World world;
	private float scale;
	private float height;
	private float width;
	
	private float direction;
	
	private Body body = null;
	private BodyDef bodyDef;
	private boolean reactToCollision;
	private boolean directionFromPhys;

	public PhysicsPosition(String id, Entity owner, Settings settings) {
		super(id, owner);

		scale = 1;
		direction = 0;

		// directions from physics or manual override?
		directionFromPhys = settings.getBoolean("directionFromPhys", true);
		
		world = (World) RegisterManager.getInstance().getOption("world");
		
		// physics settings from entitysettings
		String physShape = settings.getString("phys_shape", "Box");
		float mass = settings.getString("phys_mass").equals("INFINITE_MASS") ? Float.MAX_VALUE : 
			Float.parseFloat(settings.getString("phys_mass"));

		if(settings.getBoolean("phys_static", false)) {
			
		}
		else {
			// create body
	    	bodyDef = new BodyDef();
	    	body = world.createBody(bodyDef);
	    	
	    	ShapeDef shapeDef;
	    	
	    	// create shape
	    	if(physShape.equals("Circle")) {
		    	shapeDef = new CircleDef();
				
				// height en width
				width = settings.getFloat("width",  settings.getFloat("radius", 20.0f));
				height = settings.getFloat("height", settings.getFloat("radius", 20.0f));

		    	((CircleDef)shapeDef).radius = height;
	    	}
	    	else {
		    	// polygon box
		    	shapeDef = new PolygonDef();
				
				// height en width
				width = settings.getFloat("width",  settings.getFloat("width", 20.0f));
				height = settings.getFloat("height", settings.getFloat("height", 20.0f));
				
		    	((PolygonDef)shapeDef).setAsBox(width, height);
			}

	    	// damping: used to reduce velocity, value between 0 and 0.1
	    	body.setLinearDamping(settings.getFloat("phys_lineardamping", .01f));
	    	body.setAngularDamping(settings.getFloat("phys_angulardamping", .01f));

	    	// friction: used to reduce velocity while colliding with another body
	    	shapeDef.friction = settings.getFloat("phys_friction", 0.1f);
	    	
	    	// 
	    	shapeDef.restitution = settings.getFloat("phys_restitution", 0.1f);
	    	
	    	// mass: will be automatically calculated by the Box2d engine when not set
	    	shapeDef.density = settings.getFloat("phys_density", 1.0f);
	    	
	    	MassData md = new MassData();
	    	md.mass = mass;
	    	body.setMass(md);
	    	body.createShape(shapeDef);
	    	
	    	// is the body a bullet?
	    	body.setBullet(settings.getBoolean("phys_bullet", false));
			
			// physics settings
			reactToCollision = settings.getBoolean("phys_reacttocol", true);

			body.setUserData(owner);
		}

	}
	
	public Vector2f getXY() {
		return new Vector2f(body.getPosition().x, body.getPosition().y);
	}

	public void setXY(Vector2f position) {
		body.setXForm(new Vec2(position.getX(), position.getY()), body.getAngle());
		
		if(ConfigManager.getInstance().getActive("debugMode"))
			System.out.println("change xy from  entity '" + ((Component)body.getUserData()).getId() + 
					"' to: X=" + body.getPosition().x + " Y=" + body.getPosition().y);
	}
	
	public void setXY(float x, float y) {
		setXY(new Vector2f(x, y));
	}

	/**
	 * get the direction of the physics body in degrees
	 * 
	 * @return The direction in degrees
	 */
	public float getDirection() {
		if(directionFromPhys)
			return (float) Math.toDegrees(body.getAngle());
		else
			return direction;
	}

	public void setDirection(float direction) {
		if(directionFromPhys)
			body.m_sweep.a = (float) Math.toRadians(direction);
		else
			this.direction = direction;
	}
	
	public float getX() {
		return getXY().getX();
	}
	
	public float getY() {
		return getXY().getY();
	}
	
	public void setX(float x) {
		setXY(new Vector2f(x, getY()));
	}
	
	public void setY(float y) {
		setXY(new Vector2f(getX(), y));
	}	

	public float getScale() {
		return scale;
	}

	public void setScale(float scale) {
		this.scale = scale;
	}

	public float getHeight() {
		return height;
	}

	public void setHeight(float height) {
		this.height = height;
	}

	public float getWidth() {
		return width;
	}

	public void setWidth(float width) {
		this.width = width;
	}
	
	public void removed(Entity entity) {
		world.destroyBody(body);
	}

	public void adjustVelocity(float x, float y) {
		body.setLinearVelocity(new Vec2(x, y));
	}

	public void applyForce(float x, float y) {
		//body.setLinearVelocity(new Vec2(0, 0));
		body.applyImpulse(new Vec2(x, y), new Vec2(0, 0));
	}

	public Vector2f getVelocity() {
		return new Vector2f(body.getLinearVelocity().x, body.getLinearVelocity().y);
	}
	
	public Body getBody() {
		return body;
	}

	public boolean isReactToCollision() {
		return reactToCollision;
	}
	
}

